package com.wei.web.demo.util.limiter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.wei.web.demo.util.limiter.repository.LimiterRepository;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author buhuan.wang
 * @since 2023/3/10
 */
public class SimpleLimiter {
    private String resourceCode;
    private Long requestNum;
    private Long time;
    private TimeUnit timeUnit;
    private LimiterRepository repository;
    private Long windowTimeMS;
    private Long sliceTimeMS;

    public SimpleLimiter(String resourceCode, Integer requestNum, Integer time, TimeUnit timeUnit,
                         LimiterRepository repository) {
        this.resourceCode = resourceCode;
        this.requestNum = Long.valueOf(requestNum);
        this.time = Long.valueOf(time);
        this.timeUnit = timeUnit;
        this.repository = repository;
        windowTimeMS = timeUnit.toMillis(time);
        sliceTimeMS = windowTimeMS / 10;
    }

    public void requireOne(String resourceCode) {
        long currentTimeMS = System.currentTimeMillis();
        long currentSliceTimeMS = currentTimeMS - (currentTimeMS % sliceTimeMS);
        long startSliceTimeMS = currentSliceTimeMS + sliceTimeMS - windowTimeMS;
        System.out.println("currentTimeMS: " + currentTimeMS);
        System.out.println("currentSliceTimeMS: " + currentSliceTimeMS);
        System.out.println("startSliceTimeMS: " + startSliceTimeMS);

        repository.lockResource(resourceCode);
        try {
            String data = repository.getData(resourceCode);
            if (StringUtils.isEmpty(data)) {
                List<TimeSlice> timeWindow = new ArrayList<>();
                TimeSlice current = null;
                current = new TimeSlice();
                current.setCount(1L);
                current.setSliceTimeMS(currentSliceTimeMS);
                timeWindow.add(current);
                repository.setData(resourceCode, JSON.toJSONString(timeWindow));
            } else {
                List<TimeSlice> timeWindow = JSON.parseObject(data, new TypeReference<ArrayList<TimeSlice>>() {
                });
                timeWindow = timeWindow.stream().filter(t -> t.getSliceTimeMS() >= startSliceTimeMS).collect(Collectors.toList());
                Long nowCount = timeWindow.stream().mapToLong(t -> t.getCount()).sum();
                if (nowCount >= requestNum) {
                    throw new RuntimeException("limiter over!!!!");
                }
                TimeSlice last = timeWindow.size() > 0 ? timeWindow.get(timeWindow.size() - 1) : null;
                TimeSlice current = null;
                if (last == null || last.getSliceTimeMS() < currentSliceTimeMS) {
                    current = new TimeSlice();
                    current.setCount(1L);
                    current.setSliceTimeMS(currentSliceTimeMS);
                    timeWindow.add(current);
                } else {
                    last.setCount(last.getCount() + 1L);
                }
                repository.setData(resourceCode, JSON.toJSONString(timeWindow));
            }
        } finally {
            repository.unlockResource(resourceCode);
        }
    }

}
